package thread;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 刘远望
 * Date: 2025-05-26
 * Time: 22:23
 */
//单例模式(懒汉方式)
class SingletonLazy {
    private static volatile SingletonLazy instance = null;

    //1
    public static SingletonLazy getInstance1() {
        //这样有线程安全问题
        //在多个线程下由于这个操作不是原子的 会由于线程的随机调度引发创建实例覆盖的问题
        if (instance == null) {//防止new多个实例
            instance = new SingletonLazy();//创造时机是在第一次使用的时候 而不是在程序启动的时候(节省空间资源更推荐)
        }
        return instance;
    }

    //2
    private static Object locker = new Object();

    public static SingletonLazy getInstance2() {
        //为了解决原子性问题就要加锁
        /**
         * 引入锁之后 后执行的线程在加锁的位置阻塞 阻塞到前一个线程解锁
         * 当后一个线程进入条件的时候 前一个线程已经修改完毕
         * isnstance不会再为null 就不会后续的new
         * 两个线程两把锁(这里只有一把锁) 无法构成请求和保持 不会死锁
         */
        synchronized (locker) {//也可以再函数首部加锁
            if (instance == null) {
                instance = new SingletonLazy();
            }
        }
        /**
         * 实例创建好后 后去再调用该方法 都是直接return 如果只是进行if(判定) + 读取return 就不涉及到线程安全的问题了
         * 但是每次调用上述方法 都会触发依次加锁的操作 虽然不涉及安全问题了
         * 多线程的情况下 这里的加锁 就会相互阻塞 影响程序的执行效率
         * 看getInstance3方法优化
         */
        return instance;
    }

    //3
    public static SingletonLazy getInstance3() {
        if (instance == null) {//判断是否需要加锁
            synchronized (locker) {
                if (instance == null) {//判断是否需要new对象 两个if在单线程中执行流只有一个 if判定结果一样
                                        //在多线程中其他线程可能就会把if中的instance变量给修改了也倒是两次的if结果的结论不同
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }

    //4 方法三还是存在问题 可能存在内存可见性问题(编译器的优化非常复杂 稳妥起见加上volatile)

    //5 更关键的问题 指令重排序(编译器优化的结果) 编译器优化涉及javac JVM 有时候还需要操作系统的配合
    public static SingletonLazy getInstance4() {
        if (instance == null) {
            synchronized (locker) {
                if (instance == null) {
                    instance = new SingletonLazy();
                    /**
                     * 三步骤：1.申请内存空间 2.在空间上构造对象(初始化) 3.内存空间的首地址赋值给引用变量
                     * 正常来说按照123的顺序执行命令
                     * 但是在指令重排序的情况下会成为132这样的顺序 单线程123 132都一样 多线程可能有bug
                     * 可能会拿着未初始化的实例来进行操作
                     * 此时要加volatile解决
                     */
                    /**
                     * volatile的功能两方面
                     * 1.确保每次读取操作都是读内存
                     * 2.关于变量的读取和修改操作不会引起指令的重排序(主要)
                     */
                }
            }
        }
        return instance;
    }
}
public class Demo27 {
    public static void main(String[] args) {
        SingletonLazy t1 = SingletonLazy.getInstance1();
        SingletonLazy t2 = SingletonLazy.getInstance1();

        System.out.println(t1 == t2);
    }
}
